use crate::mem::phys_to_virt;
use core::ptr::NonNull;

use tock_registers::interfaces::{Readable, Writeable};

use crate::uart::regs::*;
use crate::uart::types::*;

unsafe impl Send for UartController {}

impl UartController {
    pub const fn new(base: *mut u8) -> Self {
        Self {
            base: NonNull::new(base).unwrap().cast(),
        }
    }

    fn get_ti_tf(clock_hz: u32, baude_rate: u32) -> (u32, u32) {
        let baude_rate_16 = 16 * baude_rate;
        let ti = clock_hz / baude_rate_16;
        let tf = clock_hz % baude_rate_16;
        let tf = (tf * 64 + (baude_rate_16 >> 1)) / baude_rate_16;
        (ti, tf)
    }

    /// no irq, no fifo, 8bits data, 1 stop bit, no odd-even check
    pub fn init_no_irq(&mut self, clock_hz: u32, baude_rate: u32) {
        // disable reg
        let regs = self.regs();
        regs.cr_l.write(CONTROLL::ENABLE::CLEAR);

        // set bd rate
        let (ti, tf) = Self::get_ti_tf(clock_hz, baude_rate);
        regs.tibd.set(ti);
        regs.tfbd.set(tf);

        // width 8 , no check, stop bit 1
        regs.cr_h.write(CONTROLH::WLEN::len8);

        // no interrupt
        regs.imsc.set(0);

        // enable uart ,rx, tx
        regs.cr_l
            .write(CONTROLL::ENABLE::SET + CONTROLL::TXE::SET + CONTROLL::RXE::SET);
    }

    const fn regs(&self) -> &UartRegs {
        unsafe { self.base.as_ref() }
    }

    pub fn read_byte_poll(&self) -> u8 {
        while self.regs().fr.read(FLAG::RXFE) != 0 {}
        (self.regs().dr.get() & 0xff) as u8
    }

    pub fn read_line_poll(&self, buf: &mut [u8]) -> usize {
        let mut len = 0;

        while len < buf.len() {
            let b = self.read_byte_poll();

            match b {
                b'\r' => continue, // skip CR
                b'\n' => break,    // end
                _ => {
                    buf[len] = b;
                    len += 1;
                }
            }
        }

        len
    }

    pub fn put_byte_poll(&mut self, b: u8) {
        while self.regs().fr.read(FLAG::BUSY) == 1 || self.regs().fr.read(FLAG::TXFF) == 1 {}
        self.regs().dr.set(b as u32);
    }

    pub fn put_line_poll(&mut self, s: &str) {
        for &b in s.as_bytes() {
            match b {
                b'\n' => {
                    self.put_byte_poll(b'\r');
                    self.put_byte_poll(b'\n');
                }
                _ => self.put_byte_poll(b),
            }
        }
    }
}

impl UartSystem {
    pub fn new() -> Self {
        let base2 = phys_to_virt(UART2_BASE).as_mut_ptr();

        Self {
            controllers: [UartController::new(&mut 0), UartController::new(base2)],
        }
    }

    pub fn controller(&mut self, index: usize) -> Option<&mut UartController> {
        match index {
            1 => Some(&mut self.controllers[1]),
            _ => None,
        }
    }
}
